package com.lx.pc.redis;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.shiro.dao.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Repository;

@SuppressWarnings("unchecked")
//@Repository("springRedisTemplate")
public class SpringRedisTemplateImpl implements SpringRedisTemplate {
	
	private final static Logger LOGGER = LoggerFactory.getLogger(SpringRedisTemplateImpl.class);
	
	public final static String SEPARATOR = ":";
	
	@Resource
	private RedisTemplate<Serializable, Serializable> redisTemplate;

	@Override
	public Boolean exists(final String key) {
		try {
			return redisTemplate.execute(new RedisCallback<Boolean>() {
				@Override
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
					try {
						return connection.exists(redisTemplate.getStringSerializer().serialize(key));
					} catch (Exception e) {
						e.printStackTrace();
					}
					return false;
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	@Override
	public void save(final String key, final Serializable object) {
		try {
			redisTemplate.execute(new RedisCallback<Serializable>() {
				@Override
				public Serializable doInRedis(RedisConnection connection) throws DataAccessException {
					try {
						RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
						connection.set(redisTemplate.getStringSerializer().serialize(key), valueSerializer.serialize(object));
					} catch (Exception e) {
						e.printStackTrace();
					}
					return null;
				}
			});
		} catch (Exception e) {
			LOGGER.error("save object error " + e.toString());
			e.printStackTrace();
		}
	}
	
	@Override
	public void save(final String key, final Serializable object, final long expireTime) {
		try {
			redisTemplate.execute(new RedisCallback<Serializable>() {
				@Override
				public Serializable doInRedis(RedisConnection connection) throws DataAccessException {
					try {
						RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
						connection.set(redisTemplate.getStringSerializer().serialize(key), valueSerializer.serialize(object));
						connection.expire(redisTemplate.getStringSerializer().serialize(key), expireTime);
					} catch (Exception e) {
						e.printStackTrace();
					}
					return null;
				}
			});
		} catch (Exception e) {
			LOGGER.error("save object error " + e.toString());
			e.printStackTrace();
		}
	}

	@Override
	public Object read(final String key) {
		try {
			return redisTemplate.execute(new RedisCallback<Object>() {
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] keybyte = redisTemplate.getStringSerializer().serialize(key);
					byte[] value = connection.get(keybyte);
					Object object = redisTemplate.getValueSerializer().deserialize(value);
					return object;
				}
			});
		} catch (Exception e) {
			LOGGER.error("read key error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Long delete(final String key) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				public Long doInRedis(RedisConnection connection) {
					return connection.del(redisTemplate.getStringSerializer().serialize(key));
				}
			});
		} catch (Exception e) {
			LOGGER.error("delete key error " + e.toString());
			e.printStackTrace();
			return  -1L;
		}
	}
	
	/*@Override
	public Long deleteByPattern(final String pattern) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				public Long doInRedis(RedisConnection connection) {
					Set<byte[]> keys = connection.keys(redisTemplate.getStringSerializer().serialize(pattern + "*"));
					if (keys.size() > 0) {
						return connection.del(ArchistArrayUtils.toArray(keys, byte[].class));
					} else {
						return 0l;
					}
				}
			});
		} catch (Exception e) {
			LOGGER.error("delete key error " + e.toString());
			e.printStackTrace();
			return  -1L;
		}
	}*/

	@Override
	public <T> Long lpush(final String key, final T... t) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				public Long doInRedis(RedisConnection connection) throws DataAccessException {
					try {
						RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
						byte value[][] = new byte[t.length][];
						for (int i = 0; i < t.length; i++) {
							value[i] = valueSerializer.serialize((Serializable) t[i]);
						}
						return connection.lPush(redisTemplate.getStringSerializer().serialize(key), value);
					} catch (Exception e) {
						e.printStackTrace();
						return -1L;
					}
				}
			});
		} catch (Exception e) {
			LOGGER.error("left push to list error " + e.toString());
			e.printStackTrace();
			return -1L;
		}
	}

	@Override
	public <T> Long rpush(final String key, final T... t) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				public Long doInRedis(RedisConnection connection) throws DataAccessException {
					RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
					byte value[][] = new byte[t.length][];
					for (int i = 0; i < t.length; i++) {
						value[i] = valueSerializer.serialize((Serializable) t[i]);
					}
					return connection.rPush(redisTemplate.getStringSerializer().serialize(key), value);
				}
			});
		} catch (Exception e) {
			LOGGER.error("right push to list error " + e.toString());
			e.printStackTrace();
			return -1L;
		}
	}

	@Override
	public <T> List<T> lrange(final String key, final int begin, final int end, final Class<T> c) {
		try {
			return redisTemplate.execute(new RedisCallback<List<T>>() {
				public List<T> doInRedis(RedisConnection connection) throws DataAccessException {
					List<T> result = new ArrayList<T>();
					byte[] keybyte = redisTemplate.getStringSerializer().serialize(key);
					List<byte[]> value = connection.lRange(keybyte, begin, end);
					for (byte[] bs : value) {
						result.add((T)redisTemplate.getValueSerializer().deserialize(bs));
					}
					return result;
				}
			});
		} catch (Exception e) {
			LOGGER.error("get list object error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Long llen(final String key) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				public Long doInRedis(RedisConnection connection) throws DataAccessException {
					return connection.lLen(redisTemplate.getStringSerializer().serialize(key));
				}
			});
		} catch (Exception e) {
			LOGGER.error("get list length error " + e.toString());
			e.printStackTrace();
			return -1L;
		}
	}

	@Override
	public Boolean hset(final String key, final String field, final Serializable value) {
		try {
			return redisTemplate.execute(new RedisCallback<Boolean>() {
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] keyByte = redisTemplate.getStringSerializer().serialize(key);
					byte[] fieldByte = redisTemplate.getStringSerializer().serialize(field);
					RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
					return connection.hSet(keyByte, fieldByte, valueSerializer.serialize(value));
				}
			});
		} catch (Exception e) {
			LOGGER.error("set hash filed value error " + e.toString());
			e.printStackTrace();
			return false;
		}
	}

	@Override
	public Object hget(final String key, final String field) {
		try {
			return redisTemplate.execute(new RedisCallback<Object>() {
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] keyByte = redisTemplate.getStringSerializer().serialize(key);
					byte[] fieldByte = redisTemplate.getStringSerializer().serialize(field);
					RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
					return valueSerializer.deserialize(connection.hGet(keyByte, fieldByte));
				}
			});
		} catch (Exception e) {
			LOGGER.error("get hash filed value error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Map<String, Object> hgetAll(final String key) {
		try {
			return redisTemplate.execute(new RedisCallback<Map<String, Object>>() {
				@Override
				public Map<String, Object> doInRedis(RedisConnection connection) throws DataAccessException {
					Map<String, Object> result = new HashMap<String, Object>();
					byte[] keyByte = redisTemplate.getStringSerializer().serialize(key);
					Map<byte[], byte[]> temp = connection.hGetAll(keyByte);
					RedisSerializer<Serializable> valueSerializer = (RedisSerializer<Serializable>) redisTemplate.getValueSerializer();
					for (byte[] b : temp.keySet()) {
						result.put(redisTemplate.getStringSerializer().deserialize(b), valueSerializer.deserialize(temp.get(b)));
					}
					return result;
				}
			});
		} catch (Exception e) {
			LOGGER.error("get hash object error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Long hdel(final String key, final String... fields) {
		try {
			return redisTemplate.execute(new RedisCallback<Long>() {
				@Override
				public Long doInRedis(RedisConnection connection) throws DataAccessException {
					byte[] keyByte = redisTemplate.getStringSerializer().serialize(key);
					byte fieldBytes[][] = new byte[fields.length][];
					for (int i = 0; i < fields.length; i++) {
						fieldBytes[i] = redisTemplate.getStringSerializer().serialize(fields[i]);
					}
					return connection.hDel(keyByte, fieldBytes);
				}
			});
		} catch (Exception e) {
			LOGGER.error("delete hash fields error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public Boolean expire(final String key, final long expireTime) {
		try {
			return redisTemplate.execute(new RedisCallback<Boolean>() {
				@Override
				public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
					return connection.expire(redisTemplate.getStringSerializer().serialize(key), expireTime);
				}
			});
		} catch (Exception e) {
			LOGGER.error("set key expire time error " + e.toString());
			e.printStackTrace();
			return false;
		}
	}
	
	@Override
	public <T> List<T> keys(final String pattern) {
		try {
			return redisTemplate.execute(new RedisCallback<List<T>>() {
				public List<T> doInRedis(RedisConnection connection) throws DataAccessException {
					List<T> result = new ArrayList<T>();
					Set<byte[]> keys = connection.keys(redisTemplate.getStringSerializer().serialize(pattern + "*"));
					for (Iterator<byte[]> iterator = keys.iterator(); iterator.hasNext();) {
						byte[] keybyte = iterator.next();
						byte[] value = connection.get(keybyte);
						T object = (T)redisTemplate.getValueSerializer().deserialize(value);
						result.add(object);
					}
					return result;
				}
			});
		} catch (Exception e) {
			LOGGER.error("get list object error " + e.toString());
			e.printStackTrace();
			return null;
		}
	}
}
